package com.lnsystem.service.impl;

import java.io.IOException;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.lf5.LogLevel;
import org.scribe.builder.ServiceBuilder;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.oauth.OAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lnsystem.constants.TKConstants;
import com.lnsystem.model.Stock;
import com.lnsystem.model.StockDailyData;
import com.lnsystem.model.StockData;
import com.lnsystem.model.StockDataType;
import com.lnsystem.model.StockWeeklyData;
import com.lnsystem.model.tk.quotedayhistorical.TKHistoricalQuoteForADay;
import com.lnsystem.model.tk.quotehistorical.Data;
import com.lnsystem.model.tk.quotehistorical.TKHistoricalQuotes;
import com.lnsystem.model.tk.quotemultiple.TKQuotes;
import com.lnsystem.model.tk.quotesingle.Quote;
import com.lnsystem.model.tk.quotesingle.Quotes;
import com.lnsystem.model.tk.quotesingle.TKQuote;
import com.lnsystem.service.QuoteService;
import com.lnsystem.service.SymbolsService;

@Component 
public class QuoteServiceTKImpl implements QuoteService {
	
	@Autowired
	SymbolsService symbolsService;

	private static Logger log = Logger.getLogger(QuoteServiceTKImpl.class);
	
	private OAuthService service;
	private Token accessToken;
	private SimpleDateFormat format = new SimpleDateFormat("y-M-d");
	
	//TODO - change this later to be injected.
	private ObjectMapper mapper = new ObjectMapper(); 
	
	// TODO change this later to be injected.
	public void initializeOAuth(){
		service = new ServiceBuilder()
        .provider(TradeKingApi.class)
        .apiKey(TKConstants.CONSUMER_KEY)
        .apiSecret(TKConstants.CONSUMER_SECRET)
        .build();
		
		accessToken = new Token(TKConstants.OAUTH_TOKEN, TKConstants.OAUTH_TOKEN_SECRET);
	}
	
	public QuoteServiceTKImpl(){
		if(service == null){
			initializeOAuth();
		}
	}
	
	
	/*
	 * {@inheritDoc}
	 */
	@Override
	public Stock getStockData(String symbol) {
		
		
		//make sure we send only one symbol in the OAuth request.
		String[] s = symbol.split(",");
		
		
		try {
				if(service != null){
					OAuthRequest request = new OAuthRequest(Verb.GET, TKConstants.QUOTES_URL+s[0]);
					service.signRequest(accessToken, request);
					Response response = request.send();
					String quoteAsJSONString = response.getBody();
					
					log.debug("Response from TK REST service: "+quoteAsJSONString);
					
		
						TKQuote tkQuote = mapper.readValue(quoteAsJSONString, TKQuote.class);
						Quotes quotes = tkQuote.getResponse().getQuotes();
						
						if(quotes!=null) {
							Stock data = new Stock();
							Quote quote = quotes.getQuote();
							
							//TODO - add more code here to fill up data..
							//TODO add code to check for nulls.
							data.setIndex(quote.getExch());
							data.setName(quote.getName());
							data.setSymbol(quote.getSymbol());
							
							return data;
						}
				}	
			} catch (Exception e) {
				log.error(ExceptionUtils.getStackTrace(e));
			}
		return null;
	}
	
	/*
	 * {@inheritDoc}
	 */
	@Override
	public StockDailyData getDailyDataForStock(String symbol) {

		StockDailyData data = new StockDailyData();

		//make sure we send only one symbol in the OAuth request.
		String[] s = symbol.split(",");
		
		if(service != null){
			OAuthRequest request = new OAuthRequest(Verb.GET, TKConstants.QUOTES_URL+s[0]);
			service.signRequest(accessToken, request);
			Response response = request.send();
			String quoteAsJSONString = response.getBody();
			
			log.debug("Response from TK REST service: "+quoteAsJSONString);
			
			try {
				TKQuote tkQuote = mapper.readValue(quoteAsJSONString, TKQuote.class);
				Quotes quotes = tkQuote.getResponse().getQuotes();
				
				if(quotes != null) {
					Quote quote = quotes.getQuote();
				
					
					//TODO - need to fill Stock object also here and attach it 
					// to StockDailyData object.
					// this will make the getStockData() method unnecessary.
					
					//data.setIndex(quote.getExch());
					//data.setName(quote.getName());
					data.setSymbol(quote.getSymbol());
					data.setOpen(Double.parseDouble(quote.getOpn()));
					data.setHigh(Double.parseDouble(quote.getHi()));
					data.setLow(Double.parseDouble(quote.getLo()));
					data.setClose(Double.parseDouble(quote.getLast()));
					data.setVolume(new BigInteger(quote.getVl()));
					data.setDate(format.parse(quote.getDate()));
					
					if(quote.getPchg_sign().equals("d")) {
						data.setPercentPriceChange(-Double.parseDouble(quote.getPchg().replace('%', ' ')));
					} else {
						data.setPercentPriceChange(Double.parseDouble(quote.getPchg().replace('%', ' ')));
					}
					
					if(quote.getChg_sign().equals("d")) {
						data.setPriceChange(-Double.parseDouble(quote.getChg()));
					} else {
						data.setPriceChange(Double.parseDouble(quote.getChg()));
					}
					
					data.setAverageVolume(new BigInteger(quote.getAdv_90()));		
					
					//TODO - if necessary add more code here to fill up data..
					//TODO add code to check for nulls.
				}

				
				
			} catch (Exception e) {
				log.error(ExceptionUtils.getStackTrace(e));
			}
		}
		return data;
	}
	
	/*
	 * {@inheritDoc}
	 */
	@Override
	public String getMultipleQuotesAsJSONString(String symbols) {
		
		String quoteAsJSONString = "";
		//String commaSeperatedList = "";
		
/*		// form a comma separated list to be sent in the URL.
		if(symbols!=null && symbols.size()>0){
			commaSeperatedList = StringUtils.join(symbols,",");
		}*/
		try {
			if(service != null){
				OAuthRequest request = new OAuthRequest(Verb.GET, TKConstants.QUOTES_URL+symbols);
				log.debug(">>>>>>> Multi-quote URL:"+TKConstants.QUOTES_URL+symbols);
				service.signRequest(accessToken, request);
				Response response = request.send();
				quoteAsJSONString = response.getBody();
				log.debug(">>>>>>> Multi-quote URL returned:"+quoteAsJSONString);
			}
		}catch(Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}
		return quoteAsJSONString;
	}
	

	
	/*
	 * {@inheritDoc}
	 */
	@Override
	public List<StockDailyData> getDailyDataForStocks() {

		long startTime = System.currentTimeMillis();
		
		List<StockDailyData> result = new ArrayList<StockDailyData>();
		
		try {
			List<String> symbols = symbolsService.getAllSymbols(true);
			
			for(String symbolsStrig : symbols) {
				
				String quoteAsJSONString = getMultipleQuotesAsJSONString(symbolsStrig);
				
				long startTime1 = System.currentTimeMillis();
				
				TKQuotes tkQuotes = mapper.readValue(quoteAsJSONString, TKQuotes.class);
				com.lnsystem.model.tk.quotemultiple.Quotes quotes = tkQuotes.getResponse().getQuotes();
				List<com.lnsystem.model.tk.quotemultiple.Quote>quote = quotes.getQuote();
				
				for(com.lnsystem.model.tk.quotemultiple.Quote q : quote ){
					
					StockDailyData dailyData = new StockDailyData();
					//stock.setIndex(q.getExch());
					//stock.setName(q.getName());
					//stock.setSymbol(q.getSymbol());
					
					//dailyData.setAverageVolume(q.get);
					dailyData.setClose(Double.parseDouble(q.getLast()));
					dailyData.setDate(format.parse(q.getDate()));
					dailyData.setHigh(Double.parseDouble(q.getHi()));
					dailyData.setLow(Double.parseDouble(q.getLo()));
					dailyData.setOpen(Double.parseDouble(q.getOpn()));
					
					
					//percentage change
					String percentageChangeString = StringUtils.remove(q.getPchg(),"%");
					if(NumberUtils.isNumber(percentageChangeString)) {
						if(q.getPchg_sign().equalsIgnoreCase("D")) {
							dailyData.setPercentPriceChange(-Double.parseDouble(percentageChangeString));
						} else {
							dailyData.setPercentPriceChange(Double.parseDouble(percentageChangeString));
						}
					}
					
					if(q.getChg_sign().equalsIgnoreCase("D")) {
						dailyData.setPriceChange(-Double.parseDouble(q.getChg()));

					}else {
						dailyData.setPriceChange(Double.parseDouble(q.getChg()));
					}
					dailyData.setSymbol(q.getSymbol());
					dailyData.setVolume(new BigInteger(q.getVl()));
					
					//dailyData.setMovingAverages(moviyngAverages);
					//dailyData.setStockId(stockId);
					
					result.add(dailyData);
					
				}
				long endTime1 = System.currentTimeMillis();
				log.info(">>>>Time taken for parsing quotesAsJSONString:"+(endTime1-startTime1)+" ms.");
			}
			
		} catch(Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}
		long endTime = System.currentTimeMillis();
		log.info(">>>Added "+result.size()+" stockdaily objects.. Time taken by getDailyDataForStocks():"+(endTime-startTime)+" ms.");

		return result;
	}
	
	/*
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<StockData> getHistoricalQuotes(String symbol, String startDate, String endDate, StockDataType interval){
		
		//TODO need to change it to stockdaily,weekly etc..
		List<StockData> historicalQuotes = new ArrayList<StockData>();
		StockData data = null;
		String historicalQuotesURL = "";
		String volume ="";
		
		if(symbol != null) {
			List<Data> dataList = new ArrayList<Data>();
			String quoteAsJSONString = "";
			
			
			//make sure we send only the first symbol in the OAuth request, create a string array s[] and send s[0].
			//String[] s = symbol.split(",");
			//String symbol = s.getSymbol();
			
			if(interval == StockDataType.DAILY) {
				historicalQuotesURL = String.format(TKConstants.HISTORICAL_QUOTES_URL, symbol,startDate,endDate,"daily");
				data = new StockDailyData();
			}else if (interval == StockDataType.WEEKLY) {
				historicalQuotesURL = String.format(TKConstants.HISTORICAL_QUOTES_URL, symbol,startDate,endDate,"weekly");
				data = new StockWeeklyData();
			}
			
			if(service != null){
				
				try {
				OAuthRequest request = new OAuthRequest(Verb.GET, historicalQuotesURL);
				service.signRequest(accessToken, request);
				Response response = request.send();
				
				//The symbol ESBA  always returned "Internal Server Error" at response.getBody()
				// Modified code to handle it and continue
				
					quoteAsJSONString = response.getBody();
				} catch(IllegalArgumentException iae) {
					log.error("Error occured while reading the getHistoricalQuote() response for symbol:"+symbol+", error details: "+iae.getLocalizedMessage());
					return Collections.<StockData>emptyList();
				} catch(Exception e) {
					log.error("I/O Error occured while getting the getHistoricalQuote() response for symbol:"+symbol+", error details: "+e.getLocalizedMessage());
					return Collections.<StockData>emptyList();	
				}
				
				
				log.debug("Response from TK historical REST service: "+quoteAsJSONString);
				
				if(quoteAsJSONString.indexOf("Invalid symbol")>=0){ // invalid symbol
					log.error("Invalid symbol found in the list: "+symbol);
					return Collections.<StockData>emptyList();
				}
				
				try {

					
					if(startDate.equals(endDate)) { // quote requested for just 1 day , so parse using different model class.
						
						TKHistoricalQuoteForADay tkHistoricalQuoteForADay = mapper.readValue(quoteAsJSONString, TKHistoricalQuoteForADay.class);
						com.lnsystem.model.tk.quotedayhistorical.Data d =  tkHistoricalQuoteForADay.getResponse().getTimeseries().getSeries().getData();
						if(interval == StockDataType.DAILY) {
							data = new StockDailyData();
						}else if (interval == StockDataType.WEEKLY) {
							data = new StockWeeklyData();
						}
					
						
						if(d.getClose()!=null){
							data.setClose(Double.parseDouble(d.getClose()));
						} else {
							data.setClose(0);
						}
						
						if(d.getOpen()!=null){
							data.setOpen(Double.parseDouble(d.getOpen()));
						} else {
							data.setOpen(0);
						}
						
						if(d.getHigh()!=null){
							data.setHigh(Double.parseDouble(d.getHigh()));
						} else {
							data.setHigh(0);
						}
						
						if(d.getLow()!=null){
							data.setLow(Double.parseDouble(d.getLow()));
						} else {
							data.setLow(0);
						}
						
						data.setSymbol(tkHistoricalQuoteForADay.getResponse().getTimeseries().getSymbol());
						
						if(d.getVolume()!=null){
							
							
							if(d.getVolume().endsWith(".000")){
								volume = StringUtils.chomp(d.getVolume(),".000");
							}
							if(StringUtils.isNotBlank(volume)){
								data.setVolume(new BigInteger(volume));
							} else {
								data.setVolume(BigInteger.ZERO);
							}

						} else {
							data.setVolume(BigInteger.ZERO);
						}
						
						if(d.getDate()!=null){
							data.setDate(format.parse(d.getDate()));
						} else {
							data.setDate(format.parse("1000-01-01"));
						}
						
						historicalQuotes.add(data);
						
						Thread.sleep(500); 
					
					} else { // time to parse historical data for more than 1 day - most likely for the last 1 year.
						TKHistoricalQuotes tkHistoricalQuotes = mapper.readValue(quoteAsJSONString, TKHistoricalQuotes.class);
						dataList = tkHistoricalQuotes.getResponse().getTimeseries().getSeries().getData();
						
						for(Data d : dataList){
							
							if(interval == StockDataType.DAILY) {
								data = new StockDailyData();
							}else if (interval == StockDataType.WEEKLY) {
								data = new StockWeeklyData();
							}
							
							if(data != null) {
								
								if(d.getClose()!=null){
									data.setClose(Double.parseDouble(d.getClose()));
								} else {
									data.setClose(0);
								}
								
								if(d.getOpen()!=null){
									data.setOpen(Double.parseDouble(d.getOpen()));
								} else {
									data.setOpen(0);
								}
								
								if(d.getHigh()!=null){
									data.setHigh(Double.parseDouble(d.getHigh()));
								} else {
									data.setHigh(0);
								}
								
								if(d.getLow()!=null){
									data.setLow(Double.parseDouble(d.getLow()));
								} else {
									data.setLow(0);
								}
								
								data.setSymbol(tkHistoricalQuotes.getResponse().getTimeseries().getSymbol());
								
								if(d.getVolume()!=null){
									
									if(d.getVolume().endsWith(".000")){
										volume = StringUtils.chomp(d.getVolume(),".000");
									}
									if(StringUtils.isNotBlank(volume)){
										data.setVolume(new BigInteger(volume));
									} else {
										data.setVolume(BigInteger.ZERO);
									}
								} else {
									data.setVolume(BigInteger.ZERO);
								}
								
								if(d.getDate()!=null){
									data.setDate(format.parse(d.getDate()));
								} else {
									data.setDate(format.parse("1000-01-01"));
								}
								
								
								//TODO - need to fill Stock object also here and attach it 
								// to StockDailyData object.
								// this will make the getStockData() method unnecessary.
								
								
			/*					if(quote.getPchg_sign().equals("d")) {
									data.setPercentPriceChange(-Double.parseDouble(quote.getPchg()));
								} else {
									data.setPercentPriceChange(Double.parseDouble(quote.getPchg()));
								}
								
								if(quote.getChg_sign().equals("d")) {
									data.setPriceChange(-Double.parseDouble(quote.getChg()));
								} else {
									data.setPriceChange(Double.parseDouble(quote.getChg()));
								}
								
								data.setAverageVolume(Double.parseDouble(quote.getAdv_90()));	*/
								
								historicalQuotes.add(data);
								
							}
						}
					}
					

					
				} catch (Exception e) {
					log.fatal("Exception occured: "+ExceptionUtils.getStackTrace(e));
					e.printStackTrace();
					return Collections.<StockData>emptyList();
				}
			}		
			
		}
		return historicalQuotes;
	}
	
	public static void main(String s[]){
		
		QuoteServiceTKImpl q = new QuoteServiceTKImpl();
		/*
		Stock stock  = q.getQuote("IBM,GOOG");
		log.info(stock.toString());
		
		String[] symbols = {"IBM","GOOG","AAPL"};
		List<Stock> stocks = q.getQuote(Arrays.asList(symbols));
		for(Stock st : stocks){
			log.info(st.toString());
		}*/
		
		//StockDailyData stockDailyData = q.getStockData("IBM");
		//System.out.println(">>>> "+stockDailyData.getClose());
		
		
		//testing with the symbol: ESBA which always returned "Internal Server Error"
		// Modified code to handle it and continue
/*		String symbolsList[] = {"ESBA","ALXN","ANIK","AMZN"};
		for(String sym : symbolsList) {
			List<StockDailyData> historical = q.getHistoricalQuotes(sym, "2014-06-05", "2014-12-31", "daily");
			for(StockDailyData st : historical){
				log.info(st.toString());
			}
		}
		*/
		Stock stock = q.getStockData("CNOB");
		System.out.println(">>>>>>>>"+stock.toString());

		
		//q.testHistoricalURL();;

	}

	public void testHistoricalURL() {
		String URL= "https://api.tradeking.com/v1/market/historical/search.json?symbols=IBM&startdate=2014-01-01&interval=daily";//&enddate=2014-12-31&interval=daily";
		if(service!=null) {
			OAuthRequest request = new OAuthRequest(Verb.GET, URL);
			service.signRequest(accessToken, request);
			Response response = request.send();
			String quoteAsJSONString = response.getBody();
			System.out.println("Response: "+quoteAsJSONString);
		}
	}
		
}

